package com.apple.reveal.filter;

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.filter.OncePerRequestFilter;

public class RequestFilter extends OncePerRequestFilter {

	private FormHttpMessageConverter formConverter = new AllEncompassingFormHttpMessageConverter();

	private boolean isFormContentType(HttpServletRequest request) {
		String contentType = request.getContentType();
		if (contentType != null) {
			try {
				MediaType mediaType = MediaType.parseMediaType(contentType);
				return (MediaType.APPLICATION_FORM_URLENCODED.includes(mediaType));
			} catch (IllegalArgumentException ex) {
				return false;
			}
		} else {
			return false;
		}
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		if (("DELETE".equalsIgnoreCase(request.getMethod())) || ("GET".equalsIgnoreCase(request.getMethod()))
				|| ("POST".equalsIgnoreCase(request.getMethod())
						|| ("PUT".equalsIgnoreCase(request.getMethod()) || "PATCH".equals(request.getMethod()))
								&& isFormContentType(request))) {
			HttpInputMessage inputMessage = new ServletServerHttpRequest(request);
			MultiValueMap<String, String> formParameters = formConverter.read(null, inputMessage);
			if (!formParameters.containsKey("b")) {
				formParameters.add("b", request.getHeader("Authorization"));
			}

			HttpServletRequest wrapper = new MyHttpFormContentRequestWrapper(request, formParameters);
			filterChain.doFilter(wrapper, response);
		} else {
			filterChain.doFilter(request, response);
		}

	}

	private static class MyHttpFormContentRequestWrapper extends HttpServletRequestWrapper {

		private MultiValueMap<String, String> formParameters;

		public MyHttpFormContentRequestWrapper(HttpServletRequest request, MultiValueMap<String, String> parameters) {
			super(request);
			this.formParameters = (parameters != null ? parameters : new LinkedMultiValueMap<String, String>());
		}

		@Override
		public String getParameter(String name) {
			String queryStringValue = super.getParameter(name);
			String formValue = this.formParameters.getFirst(name);
			return (queryStringValue != null ? queryStringValue : formValue);
		}

		@Override
		public Map<String, String[]> getParameterMap() {
			Map<String, String[]> result = new LinkedHashMap<String, String[]>();
			Enumeration<String> names = getParameterNames();
			while (names.hasMoreElements()) {
				String name = names.nextElement();
				result.put(name, getParameterValues(name));
			}
			return result;
		}

		@Override
		public Enumeration<String> getParameterNames() {
			Set<String> names = new LinkedHashSet<String>();
			names.addAll(Collections.list(super.getParameterNames()));
			names.addAll(this.formParameters.keySet());
			return Collections.enumeration(names);
		}

		@Override
		public String[] getParameterValues(String name) {
			List<String> formValues = this.formParameters.get(name);
			if (formValues == null) {
				return super.getParameterValues(name);
			} else {
				return formValues.toArray(new String[formValues.size()]);
			}
		}
	}

}
